#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include "util.hpp"

const std::string src_path = "data/input"; //html文件的目录
const std::string output = "data/raw_html/raw.txt"; //将解析后的文件内容存放到该文件中

typedef struct DocInfo
{   
    std::string title;
    std::string content;
    std::string url;
}DocInfo_t;

bool EnumFiles(const std::string& input, std::vector<std::string>* file_list);
bool ParseHtml(const std::vector<std::string>& file_list, std::vector<DocInfo_t>* results);
bool SaveHtml(const std::vector<DocInfo_t>& results, const std::string& output);

int main()
{
    //1.构建文件路径，保存至file_list中，方便一个个地读取文件
    std::vector<std::string> file_list;
    if(!EnumFiles(src_path, &file_list))
    {
        LOG(DEBUG, "Enum File failed");
        return 1;
    }
    //2.读取文件，并解析文件
    std::vector<DocInfo_t> results;
    if(!ParseHtml(file_list, &results))
    {
        LOG(DEBUG, "Parser File failed");
        return 2;
    }
    //3.将解析后的文件写入output路径的文件中
    if(!SaveHtml(results, output))
    {
        LOG(DEBUG, "Save File failed");
        return 3;
    }

    return 0;
}

bool EnumFiles(const std::string& src_path, std::vector<std::string>* file_list)
{
    //判断路径是否存在
    namespace fs = boost::filesystem;
    if(!fs::exists(src_path))
    {
        LOG(FATAL, src_path + "is not exists");
        return false;
    }
    fs::recursive_directory_iterator end;
    for(fs::recursive_directory_iterator iter(src_path);iter != end;++iter)
    {
        //判断是否是常规文件
        if(!fs::is_regular_file(iter->path()))
            continue;
        //判断是否是.html文件
        if(iter->path().extension() != ".html")
            continue;

        //std::cout << "debug: " << iter->path() << std::endl;
        file_list->push_back(iter->path().string()); 
    }

    return true;
}

static bool ParseTitle(const std::string& file, std::string* title)
{
    size_t begin = file.find("<title>");
    if(begin == std::string::npos)
        return false;
    size_t end = file.find("</title>");
    if(end == std::string::npos)
        return false;
    begin += std::string("<title>").length();
    if(begin > end)
        return false;

    *title = file.substr(begin, end - begin);

    return true;
}

static bool ParseContent(const std::string& file, std::string* content)
{
    enum status
    {
        LABEL,
        CONTENT
    };
    status s = LABEL;
    for(char c : file)
    {
        switch(s)
        {
        case LABEL:
            if(c == '>')
                s = CONTENT;
            break;
        case CONTENT:
            if(c == '<')
                s = LABEL;
            else
            {
                if(c == '\n')
                    c = ' ';
                content->push_back(c);
            }
            break;
        }
    }
    
    return true;
}

static bool ParseUrl(const std::string& file, std::string* url)
{
    std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";
    std::string url_tail = file.substr(src_path.length());
    *url = url_head + url_tail;
    
    return true;
}

static void ShowDoc(const DocInfo_t& doc)
{
    std::cout << "title: " << doc.title << std::endl;
    std::cout << "content: " << doc.content << std::endl;
    std::cout << "url: " << doc.url << std::endl;
}

bool ParseHtml(const std::vector<std::string>& file_list, std::vector<DocInfo_t>* results)
{
    for(const std::string& file : file_list)
    {
        //1.读文件
        std::string result;
        if(!Util::FileUtil::ReadFile(file, &result))
            continue;
        DocInfo_t doc;
        //2.提取title
        if(!ParseTitle(result, &doc.title))
            continue;
        //3.提取content
        if(!ParseContent(result, &doc.content))
            continue;
        //4.提取url
        if(!ParseUrl(file, &doc.url))
            continue;

        //for debug
        // ShowDoc(doc);
        // break;
        
        results->push_back(std::move(doc)); //bug
    }

    return true;
}

bool SaveHtml(const std::vector<DocInfo_t>& results, const std::string& output)
{
#define SEP '\3'
    std::ofstream ofs(output, std::ios::out | std::ios::binary);
    if(!ofs.is_open())
    {
        LOG(FATAL, "open" + output + "failed");
        return false;
    }
    for(const DocInfo_t& item : results)
    {
        std::string out_string = item.title;
        out_string += SEP;
        out_string += item.content;
        out_string += SEP;
        out_string += item.url;
        out_string += '\n';

        ofs.write(out_string.c_str(), out_string.length());
    }
    ofs.close();

    return true;
}